//////////////////////////////////////////////////////////////////////////////
//
//  Image manipulation functions (image.cpp of detours.lib)
//
//  Microsoft Research Detours Package, Version 3.0 Build_316.
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  Used for for payloads, byways, and imports.
//

#include <windows.h>
#if (_MSC_VER < 1310)
#else
#include <strsafe.h>
#endif

#if (_MSC_VER < 1299)
#pragma warning(disable: 4710)
#else
#endif

//#define DETOUR_DEBUG 1
#define DETOURS_INTERNAL

#include "detours.h"

namespace Detour
{
//////////////////////////////////////////////////////////////////////////////
//
#ifndef _STRSAFE_H_INCLUDED_
    static inline HRESULT StringCchLengthA( const char* psz, size_t cchMax, size_t* pcch )
    {
        HRESULT hr = S_OK;
        size_t cchMaxPrev = cchMax;

        if ( cchMax > 2147483647 )
        {
            return ERROR_INVALID_PARAMETER;
        }

        while ( cchMax && ( *psz != '\0' ) )
        {
            psz++;
            cchMax--;
        }

        if ( cchMax == 0 )
        {
            // the string is longer than cchMax
            hr = ERROR_INVALID_PARAMETER;
        }

        if ( SUCCEEDED( hr ) && pcch )
        {
            *pcch = cchMaxPrev - cchMax;
        }

        return hr;
    }


    static inline HRESULT StringCchCopyA( char* pszDest, size_t cchDest, const char* pszSrc )
    {
        HRESULT hr = S_OK;

        if ( cchDest == 0 )
        {
            // can not null terminate a zero-byte dest buffer
            hr = ERROR_INVALID_PARAMETER;
        }
        else
        {
            while ( cchDest && ( *pszSrc != '\0' ) )
            {
                *pszDest++ = *pszSrc++;
                cchDest--;
            }

            if ( cchDest == 0 )
            {
                // we are going to truncate pszDest
                pszDest--;
                hr = ERROR_INVALID_PARAMETER;
            }

            *pszDest = '\0';
        }

        return hr;
    }

    static inline HRESULT StringCchCatA( char* pszDest, size_t cchDest, const char* pszSrc )
    {
        HRESULT hr;
        size_t cchDestCurrent;

        if ( cchDest > 2147483647 )
        {
            return ERROR_INVALID_PARAMETER;
        }

        hr = StringCchLengthA( pszDest, cchDest, &cchDestCurrent );

        if ( SUCCEEDED( hr ) )
        {
            hr = StringCchCopyA( pszDest + cchDestCurrent,
                                 cchDest - cchDestCurrent,
                                 pszSrc );
        }

        return hr;
    }

#endif

///////////////////////////////////////////////////////////////////////////////
//
    class CImageData
    {
        friend class CImage;

    public:
        CImageData( PBYTE pbData, DWORD cbData );
        ~CImageData();

        PBYTE                   Enumerate( GUID *pGuid, DWORD *pcbData, DWORD *pnIterator );
        PBYTE                   Find( REFGUID rguid, DWORD *pcbData );
        PBYTE                   Set( REFGUID rguid, PBYTE pbData, DWORD cbData );

        BOOL                    Delete( REFGUID rguid );
        BOOL                    Purge();

        BOOL                    IsEmpty()
        {
            return m_cbData == 0;
        }
        BOOL                    IsValid();

    protected:
        BOOL                    SizeTo( DWORD cbData );

    protected:
        PBYTE                   m_pbData;
        DWORD                   m_cbData;
        DWORD                   m_cbAlloc;
    };

    class CImageImportFile
    {
        friend class CImage;
        friend class CImageImportName;

    public:
        CImageImportFile();
        ~CImageImportFile();

    public:
        CImageImportFile *      m_pNextFile;
        BOOL                    m_fByway;

        CImageImportName *      m_pImportNames;
        DWORD                   m_nImportNames;

        DWORD                   m_rvaOriginalFirstThunk;
        DWORD                   m_rvaFirstThunk;

        DWORD                   m_nForwarderChain;
        PCHAR                   m_pszOrig;
        PCHAR                   m_pszName;
    };

    class CImageImportName
    {
        friend class CImage;
        friend class CImageImportFile;

    public:
        CImageImportName();
        ~CImageImportName();

    public:
        WORD        m_nHint;
        ULONG       m_nOrig;
        ULONG       m_nOrdinal;
        PCHAR       m_pszOrig;
        PCHAR       m_pszName;
    };

    class CImage
    {
        friend class CImageThunks;
        friend class CImageChars;
        friend class CImageImportFile;
        friend class CImageImportName;

    public:
        CImage();
        ~CImage();

        static CImage *         IsValid( PDETOUR_BINARY pBinary );

    public:                                                 // File Functions
        BOOL                    Read( HANDLE hFile );
        BOOL                    Write( HANDLE hFile );
        BOOL                    Close();

    public:                                                 // Manipulation Functions
        PBYTE                   DataEnum( GUID *pGuid, DWORD *pcbData, DWORD *pnIterator );
        PBYTE                   DataFind( REFGUID rguid, DWORD *pcbData );
        PBYTE                   DataSet( REFGUID rguid, PBYTE pbData, DWORD cbData );
        BOOL                    DataDelete( REFGUID rguid );
        BOOL                    DataPurge();

        BOOL                    EditImports( PVOID pContext,
                                             PF_DETOUR_BINARY_BYWAY_CALLBACK pfBywayCallback,
                                             PF_DETOUR_BINARY_FILE_CALLBACK pfFileCallback,
                                             PF_DETOUR_BINARY_SYMBOL_CALLBACK pfSymbolCallback,
                                             PF_DETOUR_BINARY_COMMIT_CALLBACK pfCommitCallback );

    protected:
        BOOL                    WriteFile( HANDLE hFile,
                                           LPCVOID lpBuffer,
                                           DWORD nNumberOfBytesToWrite,
                                           LPDWORD lpNumberOfBytesWritten );
        BOOL                    CopyFileData( HANDLE hFile, DWORD nOldPos, DWORD cbData );
        BOOL                    ZeroFileData( HANDLE hFile, DWORD cbData );
        BOOL                    AlignFileData( HANDLE hFile );

        BOOL                    SizeOutputBuffer( DWORD cbData );
        PBYTE                   AllocateOutput( DWORD cbData, DWORD *pnVirtAddr );

        PVOID                   RvaToVa( ULONG_PTR nRva );
        DWORD                   RvaToFileOffset( DWORD nRva );

        DWORD                   FileAlign( DWORD nAddr );
        DWORD                   SectionAlign( DWORD nAddr );

        BOOL                    CheckImportsNeeded( DWORD *pnTables,
                DWORD *pnThunks,
                DWORD *pnChars );

        CImageImportFile *      NewByway( __in_z PCHAR pszName );

    private:
        DWORD                   m_dwValidSignature;
        CImageData *            m_pImageData;               // Read & Write

        HANDLE                  m_hMap;                     // Read & Write
        PBYTE                   m_pMap;                     // Read & Write

        DWORD                   m_nNextFileAddr;            // Write
        DWORD                   m_nNextVirtAddr;            // Write

        IMAGE_DOS_HEADER        m_DosHeader;                // Read & Write
        IMAGE_NT_HEADERS        m_NtHeader;                 // Read & Write
        IMAGE_SECTION_HEADER    m_SectionHeaders[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];

        DWORD                   m_nPrePE;
        DWORD                   m_cbPrePE;
        DWORD                   m_cbPostPE;

        DWORD                   m_nPeOffset;
        DWORD                   m_nSectionsOffset;
        DWORD                   m_nExtraOffset;
        DWORD                   m_nFileSize;

        DWORD                   m_nOutputVirtAddr;
        DWORD                   m_nOutputVirtSize;
        DWORD                   m_nOutputFileAddr;

        PBYTE                   m_pbOutputBuffer;
        DWORD                   m_cbOutputBuffer;

        CImageImportFile *      m_pImportFiles;
        DWORD                   m_nImportFiles;

        BOOL                    m_fHadDetourSection;

    private:
        enum
        {
            DETOUR_IMAGE_VALID_SIGNATURE = 0xfedcba01,      // "Dtr\0"
        };
    };

//////////////////////////////////////////////////////////////////////////////
//
    static BYTE s_rbDosCode[0x10] =
    {
        0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD,
        0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, '*', '*'
    };

    static inline DWORD Max( DWORD a, DWORD b )
    {
        return a > b ? a : b;
    }

    static inline DWORD Align( DWORD a, DWORD size )
    {
        size--;
        return ( a + size ) & ~size;
    }

    static inline DWORD QuadAlign( DWORD a )
    {
        return Align( a, 8 );
    }

    static PCHAR DuplicateString( __in_z PCHAR pszIn )
    {
        if ( pszIn )
        {
            UINT nIn = ( UINT )strlen( pszIn );
            PCHAR pszOut = new CHAR [nIn + 1];

            if ( pszOut == NULL )
            {
                SetLastError( ERROR_OUTOFMEMORY );
            }
            else
            {
                CopyMemory( pszOut, pszIn, nIn + 1 );
            }

            return pszOut;
        }

        return NULL;
    }

    static PCHAR ReplaceString( __deref_out PCHAR *ppsz, __in_z PCHAR pszIn )
    {
        if ( ppsz == NULL )
        {
            return NULL;
        }

        UINT nIn;

        if ( *ppsz != NULL )
        {
            if ( strcmp( *ppsz, pszIn ) == 0 )
            {
                return *ppsz;
            }

            nIn = ( UINT )strlen( pszIn );

            if ( strlen( *ppsz ) == nIn )
            {
                CopyMemory( *ppsz, pszIn, nIn + 1 );
                return *ppsz;
            }
            else
            {
                delete[] *ppsz;
                *ppsz = NULL;
            }
        }
        else
        {
            nIn = ( UINT )strlen( pszIn );
        }

        *ppsz = new CHAR [nIn + 1];

        if ( *ppsz == NULL )
        {
            SetLastError( ERROR_OUTOFMEMORY );
        }
        else
        {
            CopyMemory( *ppsz, pszIn, nIn + 1 );
        }

        return *ppsz;
    }

//////////////////////////////////////////////////////////////////////////////
//
    CImageImportFile::CImageImportFile()
    {
        m_pNextFile = NULL;
        m_fByway = FALSE;

        m_pImportNames = NULL;
        m_nImportNames = 0;

        m_rvaOriginalFirstThunk = 0;
        m_rvaFirstThunk = 0;

        m_nForwarderChain = ( UINT )0;
        m_pszName = NULL;
        m_pszOrig = NULL;
    }

    CImageImportFile::~CImageImportFile()
    {
        if ( m_pNextFile )
        {
            delete m_pNextFile;
            m_pNextFile = NULL;
        }

        if ( m_pImportNames )
        {
            delete[] m_pImportNames;
            m_pImportNames = NULL;
            m_nImportNames = 0;
        }

        if ( m_pszName )
        {
            delete[] m_pszName;
            m_pszName = NULL;
        }

        if ( m_pszOrig )
        {
            delete[] m_pszOrig;
            m_pszOrig = NULL;
        }
    }

    CImageImportName::CImageImportName()
    {
        m_nOrig = 0;
        m_nOrdinal = 0;
        m_nHint = 0;
        m_pszName = NULL;
        m_pszOrig = NULL;
    }

    CImageImportName::~CImageImportName()
    {
        if ( m_pszName )
        {
            delete[] m_pszName;
            m_pszName = NULL;
        }

        if ( m_pszOrig )
        {
            delete[] m_pszOrig;
            m_pszOrig = NULL;
        }
    }

//////////////////////////////////////////////////////////////////////////////
//
    CImageData::CImageData( PBYTE pbData, DWORD cbData )
    {
        m_pbData = pbData;
        m_cbData = cbData;
        m_cbAlloc = 0;
    }

    CImageData::~CImageData()
    {
        IsValid();

        if ( m_cbAlloc == 0 )
        {
            m_pbData = NULL;
        }

        if ( m_pbData )
        {
            delete[] m_pbData;
            m_pbData = NULL;
        }

        m_cbData = 0;
        m_cbAlloc = 0;
    }

    BOOL CImageData::SizeTo( DWORD cbData )
    {
        IsValid();

        if ( cbData <= m_cbAlloc )
        {
            return TRUE;
        }

        PBYTE pbNew = new BYTE [cbData];

        if ( pbNew == NULL )
        {
            SetLastError( ERROR_OUTOFMEMORY );
            return FALSE;
        }

        if ( m_pbData )
        {
            CopyMemory( pbNew, m_pbData, m_cbData );

            if ( m_cbAlloc > 0 )
            {
                delete[] m_pbData;
            }

            m_pbData = NULL;
        }

        m_pbData = pbNew;
        m_cbAlloc = cbData;

        IsValid();

        return TRUE;
    }

    BOOL CImageData::Purge()
    {
        m_cbData = 0;

        IsValid();

        return TRUE;
    }

    BOOL CImageData::IsValid()
    {
        if ( m_pbData == NULL )
        {
            return TRUE;
        }

        PBYTE pbBeg = m_pbData;
        PBYTE pbEnd = m_pbData + m_cbData;

        for ( PBYTE pbIter = pbBeg; pbIter < pbEnd; )
        {
            PDETOUR_SECTION_RECORD pRecord = ( PDETOUR_SECTION_RECORD )pbIter;

            if ( pRecord->cbBytes < sizeof( DETOUR_SECTION_RECORD ) )
            {
                return FALSE;
            }

            if ( pRecord->nReserved != 0 )
            {
                return FALSE;
            }

            pbIter += pRecord->cbBytes;
        }

        return TRUE;
    }

    PBYTE CImageData::Enumerate( GUID *pGuid, DWORD *pcbData, DWORD *pnIterator )
    {
        IsValid();

        if ( pnIterator == NULL ||
                m_cbData < *pnIterator + sizeof( DETOUR_SECTION_RECORD ) )
        {

            if ( pcbData )
            {
                *pcbData = 0;
            }

            if ( pGuid )
            {
                ZeroMemory( pGuid, sizeof( *pGuid ) );
            }

            return NULL;
        }

        PDETOUR_SECTION_RECORD pRecord = ( PDETOUR_SECTION_RECORD )( m_pbData + *pnIterator );

        if ( pGuid )
        {
            *pGuid = pRecord->guid;
        }

        if ( pcbData )
        {
            *pcbData = pRecord->cbBytes - sizeof( DETOUR_SECTION_RECORD );
        }

        *pnIterator = ( LONG )( ( ( PBYTE )pRecord - m_pbData ) + pRecord->cbBytes );

        return ( PBYTE )( pRecord + 1 );
    }

    PBYTE CImageData::Find( REFGUID rguid, DWORD *pcbData )
    {
        IsValid();

        DWORD cbBytes = sizeof( DETOUR_SECTION_RECORD );

        for ( DWORD nOffset = 0; nOffset < m_cbData; nOffset += cbBytes )
        {
            PDETOUR_SECTION_RECORD pRecord = ( PDETOUR_SECTION_RECORD )( m_pbData + nOffset );

            cbBytes = pRecord->cbBytes;

            if ( cbBytes > m_cbData )
            {
                break;
            }

            if ( cbBytes < sizeof( DETOUR_SECTION_RECORD ) )
            {
                continue;
            }

            if ( pRecord->guid.Data1 == rguid.Data1 &&
                    pRecord->guid.Data2 == rguid.Data2 &&
                    pRecord->guid.Data3 == rguid.Data3 &&
                    pRecord->guid.Data4[0] == rguid.Data4[0] &&
                    pRecord->guid.Data4[1] == rguid.Data4[1] &&
                    pRecord->guid.Data4[2] == rguid.Data4[2] &&
                    pRecord->guid.Data4[3] == rguid.Data4[3] &&
                    pRecord->guid.Data4[4] == rguid.Data4[4] &&
                    pRecord->guid.Data4[5] == rguid.Data4[5] &&
                    pRecord->guid.Data4[6] == rguid.Data4[6] &&
                    pRecord->guid.Data4[7] == rguid.Data4[7] )
            {

                *pcbData = cbBytes - sizeof( DETOUR_SECTION_RECORD );
                return ( PBYTE )( pRecord + 1 );
            }
        }

        if ( pcbData )
        {
            *pcbData = 0;
        }

        return NULL;
    }

    BOOL CImageData::Delete( REFGUID rguid )
    {
        IsValid();

        PBYTE pbFound = NULL;
        DWORD cbFound = 0;

        pbFound = Find( rguid, &cbFound );

        if ( pbFound == NULL )
        {
            SetLastError( ERROR_MOD_NOT_FOUND );
            return FALSE;
        }

        pbFound -= sizeof( DETOUR_SECTION_RECORD );
        cbFound += sizeof( DETOUR_SECTION_RECORD );

        PBYTE pbRestData = pbFound + cbFound;
        DWORD cbRestData = m_cbData - ( LONG )( pbRestData - m_pbData );

        if ( cbRestData )
        {
            MoveMemory( pbFound, pbRestData, cbRestData );
        }

        m_cbData -= cbFound;

        IsValid();
        return TRUE;
    }

    PBYTE CImageData::Set( REFGUID rguid, PBYTE pbData, DWORD cbData )
    {
        IsValid();
        Delete( rguid );

        DWORD cbAlloc = QuadAlign( cbData );

        if ( !SizeTo( m_cbData + cbAlloc + sizeof( DETOUR_SECTION_RECORD ) ) )
        {
            return NULL;
        }

        PDETOUR_SECTION_RECORD pRecord = ( PDETOUR_SECTION_RECORD )( m_pbData + m_cbData );
        pRecord->cbBytes = cbAlloc + sizeof( DETOUR_SECTION_RECORD );
        pRecord->nReserved = 0;
        pRecord->guid = rguid;

        PBYTE pbDest = ( PBYTE )( pRecord + 1 );

        if ( pbData )
        {
            CopyMemory( pbDest, pbData, cbData );

            if ( cbData < cbAlloc )
            {
                ZeroMemory( pbDest + cbData, cbAlloc - cbData );
            }
        }
        else
        {
            if ( cbAlloc > 0 )
            {
                ZeroMemory( pbDest, cbAlloc );
            }
        }

        m_cbData += cbAlloc + sizeof( DETOUR_SECTION_RECORD );

        IsValid();
        return pbDest;
    }

//////////////////////////////////////////////////////////////////////////////
//
    class CImageThunks
    {
    private:
        CImage *            m_pImage;
        PIMAGE_THUNK_DATA   m_pThunks;
        DWORD               m_nThunks;
        DWORD               m_nThunksMax;
        DWORD               m_nThunkVirtAddr;

    public:
        CImageThunks( CImage *pImage, DWORD nThunksMax, DWORD *pnAddr )
        {
            m_pImage = pImage;
            m_nThunks = 0;
            m_nThunksMax = nThunksMax;
            m_pThunks = ( PIMAGE_THUNK_DATA )
                        m_pImage->AllocateOutput( sizeof( IMAGE_THUNK_DATA ) * nThunksMax,
                                                  &m_nThunkVirtAddr );
            *pnAddr = m_nThunkVirtAddr;
        }

        PIMAGE_THUNK_DATA Current( DWORD *pnVirtAddr )
        {
            if ( m_nThunksMax > 1 )
            {
                *pnVirtAddr = m_nThunkVirtAddr;
                return m_pThunks;
            }

            *pnVirtAddr = 0;
            return NULL;
        }

        PIMAGE_THUNK_DATA Allocate( ULONG_PTR nData, DWORD *pnVirtAddr )
        {
            if ( m_nThunks < m_nThunksMax )
            {
                *pnVirtAddr = m_nThunkVirtAddr;

                m_nThunks++;
                m_nThunkVirtAddr += sizeof( IMAGE_THUNK_DATA );
                m_pThunks->u1.Ordinal = nData;
                return m_pThunks++;
            }

            *pnVirtAddr = 0;
            return NULL;
        }

        DWORD   Size()
        {
            return m_nThunksMax * sizeof( IMAGE_THUNK_DATA );
        }
    };

//////////////////////////////////////////////////////////////////////////////
//
    class CImageChars
    {
    private:
        CImage *        m_pImage;
        PCHAR           m_pChars;
        DWORD           m_nChars;
        DWORD           m_nCharsMax;
        DWORD           m_nCharVirtAddr;

    public:
        CImageChars( CImage *pImage, DWORD nCharsMax, DWORD *pnAddr )
        {
            m_pImage = pImage;
            m_nChars = 0;
            m_nCharsMax = nCharsMax;
            m_pChars = ( PCHAR )m_pImage->AllocateOutput( nCharsMax, &m_nCharVirtAddr );
            *pnAddr = m_nCharVirtAddr;
        }

        PCHAR Allocate( __in_z PCHAR pszString, DWORD *pnVirtAddr )
        {
            DWORD nLen = ( DWORD )strlen( pszString ) + 1;
            nLen += ( nLen & 1 );

            if ( m_nChars + nLen > m_nCharsMax )
            {
                *pnVirtAddr = 0;
                return NULL;
            }

            *pnVirtAddr = m_nCharVirtAddr;
            HRESULT hrRet = StringCchCopyA( m_pChars, m_nCharsMax, pszString );

            if ( FAILED( hrRet ) )
            {
                return NULL;
            }

            pszString = m_pChars;

            m_pChars += nLen;
            m_nChars += nLen;
            m_nCharVirtAddr += nLen;

            return pszString;
        }

        PCHAR Allocate( PCHAR pszString, DWORD nHint, DWORD *pnVirtAddr )
        {
            DWORD nLen = ( DWORD )strlen( pszString ) + 1 + sizeof( USHORT );
            nLen += ( nLen & 1 );

            if ( m_nChars + nLen > m_nCharsMax )
            {
                *pnVirtAddr = 0;
                return NULL;
            }

            *pnVirtAddr = m_nCharVirtAddr;
            *( USHORT * )m_pChars = ( USHORT )nHint;

            HRESULT hrRet = StringCchCopyA( m_pChars + sizeof( USHORT ), m_nCharsMax, pszString );

            if ( FAILED( hrRet ) )
            {
                return NULL;
            }

            pszString = m_pChars + sizeof( USHORT );

            m_pChars += nLen;
            m_nChars += nLen;
            m_nCharVirtAddr += nLen;

            return pszString;
        }

        DWORD Size()
        {
            return m_nChars;
        }
    };

//////////////////////////////////////////////////////////////////////////////
//
    CImage * CImage::IsValid( PDETOUR_BINARY pBinary )
    {
        if ( pBinary )
        {
            CImage *pImage = ( CImage * )pBinary;

            if ( pImage->m_dwValidSignature == DETOUR_IMAGE_VALID_SIGNATURE )
            {
                return pImage;
            }
        }

        SetLastError( ERROR_INVALID_HANDLE );
        return NULL;
    }

    CImage::CImage()
    {
        m_dwValidSignature = ( DWORD )DETOUR_IMAGE_VALID_SIGNATURE;

        m_hMap = NULL;
        m_pMap = NULL;

        m_nPeOffset = 0;
        m_nSectionsOffset = 0;

        m_pbOutputBuffer = NULL;
        m_cbOutputBuffer = 0;

        m_pImageData = NULL;

        m_pImportFiles = NULL;
        m_nImportFiles = 0;

        m_fHadDetourSection = FALSE;
    }

    CImage::~CImage()
    {
        Close();
        m_dwValidSignature = 0;
    }

    BOOL CImage::Close()
    {
        if ( m_pImportFiles )
        {
            delete m_pImportFiles;
            m_pImportFiles = NULL;
            m_nImportFiles = 0;
        }

        if ( m_pImageData )
        {
            delete m_pImageData;
            m_pImageData = NULL;
        }

        if ( m_pMap != NULL )
        {
            UnmapViewOfFile( m_pMap );
            m_pMap = NULL;
        }

        if ( m_hMap )
        {
            CloseHandle( m_hMap );
            m_hMap = NULL;
        }

        if ( m_pbOutputBuffer )
        {
            delete[] m_pbOutputBuffer;
            m_pbOutputBuffer = NULL;
            m_cbOutputBuffer = 0;
        }

        return TRUE;
    }

//////////////////////////////////////////////////////////////////////////////
//
    PBYTE CImage::DataEnum( GUID *pGuid, DWORD *pcbData, DWORD *pnIterator )
    {
        if ( m_pImageData == NULL )
        {
            return NULL;
        }

        return m_pImageData->Enumerate( pGuid, pcbData, pnIterator );
    }

    PBYTE CImage::DataFind( REFGUID rguid, DWORD *pcbData )
    {
        if ( m_pImageData == NULL )
        {
            return NULL;
        }

        return m_pImageData->Find( rguid, pcbData );
    }

    PBYTE CImage::DataSet( REFGUID rguid, PBYTE pbData, DWORD cbData )
    {
        if ( m_pImageData == NULL )
        {
            return NULL;
        }

        return m_pImageData->Set( rguid, pbData, cbData );
    }

    BOOL CImage::DataDelete( REFGUID rguid )
    {
        if ( m_pImageData == NULL )
        {
            return FALSE;
        }

        return m_pImageData->Delete( rguid );
    }

    BOOL CImage::DataPurge()
    {
        if ( m_pImageData == NULL )
        {
            return TRUE;
        }

        return m_pImageData->Purge();
    }

//////////////////////////////////////////////////////////////////////////////
//
    BOOL CImage::SizeOutputBuffer( DWORD cbData )
    {
        if ( m_cbOutputBuffer < cbData )
        {
            if ( cbData < 1024 ) //65536
            {
                cbData = 1024;
            }

            cbData = FileAlign( cbData );

            PBYTE pOutput = new BYTE [cbData];

            if ( pOutput == NULL )
            {
                SetLastError( ERROR_OUTOFMEMORY );
                return FALSE;
            }

            if ( m_pbOutputBuffer )
            {
                CopyMemory( pOutput, m_pbOutputBuffer, m_cbOutputBuffer );

                delete[] m_pbOutputBuffer;
                m_pbOutputBuffer = NULL;
            }

            ZeroMemory( pOutput + m_cbOutputBuffer, cbData - m_cbOutputBuffer ),

                        m_pbOutputBuffer = pOutput;
            m_cbOutputBuffer = cbData;
        }

        return TRUE;
    }

    PBYTE CImage::AllocateOutput( DWORD cbData, DWORD *pnVirtAddr )
    {
        cbData = QuadAlign( cbData );

        PBYTE pbData = m_pbOutputBuffer + m_nOutputVirtSize;

        *pnVirtAddr = m_nOutputVirtAddr + m_nOutputVirtSize;
        m_nOutputVirtSize += cbData;

        if ( m_nOutputVirtSize > m_cbOutputBuffer )
        {
            SetLastError( ERROR_OUTOFMEMORY );
            return NULL;
        }

        ZeroMemory( pbData, cbData );

        return pbData;
    }

//////////////////////////////////////////////////////////////////////////////
//
    DWORD CImage::FileAlign( DWORD nAddr )
    {
        return Align( nAddr, m_NtHeader.OptionalHeader.FileAlignment );
    }

    DWORD CImage::SectionAlign( DWORD nAddr )
    {
        return Align( nAddr, m_NtHeader.OptionalHeader.SectionAlignment );
    }

//////////////////////////////////////////////////////////////////////////////
//
    PVOID CImage::RvaToVa( ULONG_PTR nRva )
    {
        if ( nRva == 0 )
        {
            return NULL;
        }

        for ( DWORD n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++ )
        {
            DWORD vaStart = m_SectionHeaders[n].VirtualAddress;
            DWORD vaEnd = vaStart + m_SectionHeaders[n].SizeOfRawData;

            if ( nRva >= vaStart && nRva < vaEnd )
            {
                return ( PBYTE )m_pMap
                       + m_SectionHeaders[n].PointerToRawData
                       + nRva - m_SectionHeaders[n].VirtualAddress;
            }
        }

        return NULL;
    }

    DWORD CImage::RvaToFileOffset( DWORD nRva )
    {
        DWORD n;

        for ( n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++ )
        {
            DWORD vaStart = m_SectionHeaders[n].VirtualAddress;
            DWORD vaEnd = vaStart + m_SectionHeaders[n].SizeOfRawData;

            if ( nRva >= vaStart && nRva < vaEnd )
            {
                return m_SectionHeaders[n].PointerToRawData
                       + nRva - m_SectionHeaders[n].VirtualAddress;
            }
        }

        return 0;
    }

//////////////////////////////////////////////////////////////////////////////
//
    BOOL CImage::WriteFile( HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
                            LPDWORD lpNumberOfBytesWritten )
    {
        return ::WriteFile( hFile,
                            lpBuffer,
                            nNumberOfBytesToWrite,
                            lpNumberOfBytesWritten,
                            NULL );
    }


    BOOL CImage::CopyFileData( HANDLE hFile, DWORD nOldPos, DWORD cbData )
    {
        DWORD cbDone = 0;
        return WriteFile( hFile, m_pMap + nOldPos, cbData, &cbDone );
    }

    BOOL CImage::ZeroFileData( HANDLE hFile, DWORD cbData )
    {
        if ( !SizeOutputBuffer( 4096 ) )
        {
            return FALSE;
        }

        ZeroMemory( m_pbOutputBuffer, 4096 );

        for ( DWORD cbLeft = cbData; cbLeft > 0; )
        {
            DWORD cbStep = cbLeft > sizeof( m_pbOutputBuffer )
                           ? sizeof( m_pbOutputBuffer ) : cbLeft;
            DWORD cbDone = 0;

            if ( !WriteFile( hFile, m_pbOutputBuffer, cbStep, &cbDone ) )
            {
                return FALSE;
            }

            if ( cbDone == 0 )
            {
                break;
            }

            cbLeft -= cbDone;
        }

        return TRUE;
    }

    BOOL CImage::AlignFileData( HANDLE hFile )
    {
        DWORD nLastFileAddr = m_nNextFileAddr;

        m_nNextFileAddr = FileAlign( m_nNextFileAddr );
        m_nNextVirtAddr = SectionAlign( m_nNextVirtAddr );

        if ( hFile != INVALID_HANDLE_VALUE )
        {
            if ( m_nNextFileAddr > nLastFileAddr )
            {
                if ( SetFilePointer( hFile, nLastFileAddr, NULL, FILE_BEGIN ) == ~0u )
                {
                    return FALSE;
                }

                return ZeroFileData( hFile, m_nNextFileAddr - nLastFileAddr );
            }
        }

        return TRUE;
    }

    BOOL CImage::Read( HANDLE hFile )
    {
        DWORD n;
        PBYTE pbData = NULL;
        DWORD cbData = 0;

        if ( hFile == INVALID_HANDLE_VALUE )
        {
            SetLastError( ERROR_INVALID_HANDLE );
            return FALSE;
        }

        ///////////////////////////////////////////////////////// Create mapping.
        //
        m_nFileSize = GetFileSize( hFile, NULL );

        if ( m_nFileSize == ( DWORD ) - 1 )
        {
            return FALSE;
        }

        m_hMap = CreateFileMappingW( hFile, NULL, PAGE_READONLY, 0, 0, NULL );

        if ( m_hMap == NULL )
        {
            return FALSE;
        }

        m_pMap = ( PBYTE )MapViewOfFileEx( m_hMap, FILE_MAP_READ, 0, 0, 0, NULL );

        if ( m_pMap == NULL )
        {
            return FALSE;
        }

        ////////////////////////////////////////////////////// Process DOS Header.
        //
        PIMAGE_DOS_HEADER pDosHeader = ( PIMAGE_DOS_HEADER )m_pMap;

        if ( pDosHeader->e_magic != IMAGE_DOS_SIGNATURE )
        {
            SetLastError( ERROR_BAD_EXE_FORMAT );
            return FALSE;
        }

        m_nPeOffset = pDosHeader->e_lfanew;
        m_nPrePE = 0;
        m_cbPrePE = QuadAlign( pDosHeader->e_lfanew );

        CopyMemory( &m_DosHeader, m_pMap + m_nPrePE, sizeof( m_DosHeader ) );

        /////////////////////////////////////////////////////// Process PE Header.
        //
        CopyMemory( &m_NtHeader, m_pMap + m_nPeOffset, sizeof( m_NtHeader ) );

        if ( m_NtHeader.Signature != IMAGE_NT_SIGNATURE )
        {
            SetLastError( ERROR_INVALID_EXE_SIGNATURE );
            return FALSE;
        }

        if ( m_NtHeader.FileHeader.SizeOfOptionalHeader == 0 )
        {
            SetLastError( ERROR_EXE_MARKED_INVALID );
            return FALSE;
        }

        m_nSectionsOffset = m_nPeOffset
                            + sizeof( m_NtHeader.Signature )
                            + sizeof( m_NtHeader.FileHeader )
                            + m_NtHeader.FileHeader.SizeOfOptionalHeader;

        ///////////////////////////////////////////////// Process Section Headers.
        //
        if ( m_NtHeader.FileHeader.NumberOfSections > ( sizeof( m_SectionHeaders ) /
                sizeof( m_SectionHeaders[0] ) ) )
        {
            SetLastError( ERROR_EXE_MARKED_INVALID );
            return FALSE;
        }

        CopyMemory( &m_SectionHeaders,
                    m_pMap + m_nSectionsOffset,
                    sizeof( m_SectionHeaders[0] ) * m_NtHeader.FileHeader.NumberOfSections );

        /////////////////////////////////////////////////// Parse .detour Section.
        //
        DWORD rvaOriginalImageDirectory = 0;
        DWORD rvaDetourBeg = 0;
        DWORD rvaDetourEnd = 0;

        for ( n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++ )
        {
            if ( strcmp( ( PCHAR )m_SectionHeaders[n].Name, ".detour" ) == 0 )
            {
                DETOUR_SECTION_HEADER dh;
                CopyMemory( &dh,
                            m_pMap + m_SectionHeaders[n].PointerToRawData,
                            sizeof( dh ) );

                rvaOriginalImageDirectory = dh.nOriginalImportVirtualAddress;

                if ( dh.cbPrePE != 0 )
                {
                    m_nPrePE = m_SectionHeaders[n].PointerToRawData + sizeof( dh );
                    m_cbPrePE = dh.cbPrePE;
                }

                rvaDetourBeg = m_SectionHeaders[n].VirtualAddress;
                rvaDetourEnd = rvaDetourBeg + m_SectionHeaders[n].SizeOfRawData;
            }
        }

        //////////////////////////////////////////////////////// Get Import Table.
        //
        DWORD rvaImageDirectory = m_NtHeader.OptionalHeader
                                  .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
        PIMAGE_IMPORT_DESCRIPTOR iidp
            = ( PIMAGE_IMPORT_DESCRIPTOR )RvaToVa( rvaImageDirectory );
        PIMAGE_IMPORT_DESCRIPTOR oidp
            = ( PIMAGE_IMPORT_DESCRIPTOR )RvaToVa( rvaOriginalImageDirectory );

        if ( oidp == NULL )
        {
            oidp = iidp;
        }

        if ( iidp == NULL || oidp == NULL )
        {
            SetLastError( ERROR_EXE_MARKED_INVALID );
            return FALSE;
        }

        DWORD nFiles = 0;

        for ( ; iidp[nFiles].OriginalFirstThunk != 0 || iidp[nFiles].FirstThunk != 0; nFiles++ )
        {
        }

        CImageImportFile **ppLastFile = &m_pImportFiles;
        m_pImportFiles = NULL;

        for ( n = 0; n < nFiles; n++, iidp++ )
        {
            ULONG_PTR rvaName = iidp->Name;
            PCHAR pszName = ( PCHAR )RvaToVa( rvaName );

            if ( pszName == NULL )
            {
                SetLastError( ERROR_EXE_MARKED_INVALID );
                goto fail;
            }

            CImageImportFile *pImportFile = new CImageImportFile;

            if ( pImportFile == NULL )
            {
                SetLastError( ERROR_OUTOFMEMORY );
                goto fail;
            }

            *ppLastFile = pImportFile;
            ppLastFile = &pImportFile->m_pNextFile;
            m_nImportFiles++;

            pImportFile->m_pszName = DuplicateString( pszName );

            if ( pImportFile->m_pszName == NULL )
            {
                goto fail;
            }

            pImportFile->m_rvaOriginalFirstThunk = iidp->OriginalFirstThunk;
            pImportFile->m_rvaFirstThunk = iidp->FirstThunk;
            pImportFile->m_nForwarderChain = iidp->ForwarderChain;
            pImportFile->m_pImportNames = NULL;
            pImportFile->m_nImportNames = 0;
            pImportFile->m_fByway = FALSE;

            if ( ( ULONG )iidp->FirstThunk >= rvaDetourBeg &&
                    ( ULONG )iidp->FirstThunk < rvaDetourEnd )
            {

                pImportFile->m_pszOrig = NULL;
                pImportFile->m_fByway = TRUE;
                continue;
            }

            rvaName = oidp->Name;
            pszName = ( PCHAR )RvaToVa( rvaName );

            if ( pszName == NULL )
            {
                SetLastError( ERROR_EXE_MARKED_INVALID );
                goto fail;
            }

            pImportFile->m_pszOrig = DuplicateString( pszName );

            if ( pImportFile->m_pszOrig == NULL )
            {
                goto fail;
            }

            DWORD rvaThunk = iidp->OriginalFirstThunk;

            if ( !rvaThunk )
            {
                rvaThunk = iidp->FirstThunk;
            }

            PIMAGE_THUNK_DATA pAddrThunk = ( PIMAGE_THUNK_DATA )RvaToVa( rvaThunk );
            rvaThunk = oidp->OriginalFirstThunk;

            if ( !rvaThunk )
            {
                rvaThunk = oidp->FirstThunk;
            }

            PIMAGE_THUNK_DATA pLookThunk = ( PIMAGE_THUNK_DATA )RvaToVa( rvaThunk );

            DWORD nNames = 0;

            if ( pAddrThunk )
            {
                for ( ; pAddrThunk[nNames].u1.Ordinal; nNames++ )
                {
                }
            }

            if ( pAddrThunk && nNames )
            {
                pImportFile->m_nImportNames = nNames;
                pImportFile->m_pImportNames = new CImageImportName [nNames];

                if ( pImportFile->m_pImportNames == NULL )
                {
                    SetLastError( ERROR_OUTOFMEMORY );
                    goto fail;
                }

                CImageImportName *pImportName = &pImportFile->m_pImportNames[0];

                for ( DWORD f = 0; f < nNames; f++, pImportName++ )
                {
                    pImportName->m_nOrig = 0;
                    pImportName->m_nOrdinal = 0;
                    pImportName->m_nHint = 0;
                    pImportName->m_pszName = NULL;
                    pImportName->m_pszOrig = NULL;

                    rvaName = pAddrThunk[f].u1.Ordinal;

                    if ( rvaName & IMAGE_ORDINAL_FLAG )
                    {
                        pImportName->m_nOrig = ( ULONG )IMAGE_ORDINAL( rvaName );
                        pImportName->m_nOrdinal = pImportName->m_nOrig;
                    }
                    else
                    {
                        PIMAGE_IMPORT_BY_NAME pName
                            = ( PIMAGE_IMPORT_BY_NAME )RvaToVa( rvaName );

                        if ( pName )
                        {
                            pImportName->m_nHint = pName->Hint;
                            pImportName->m_pszName = DuplicateString( ( PCHAR )pName->Name );

                            if ( pImportName->m_pszName == NULL )
                            {
                                goto fail;
                            }
                        }

                        rvaName = pLookThunk[f].u1.Ordinal;

                        if ( rvaName & IMAGE_ORDINAL_FLAG )
                        {
                            pImportName->m_nOrig = ( ULONG )IMAGE_ORDINAL( rvaName );
                            pImportName->m_nOrdinal = ( ULONG )IMAGE_ORDINAL( rvaName );
                        }
                        else
                        {
                            pName = ( PIMAGE_IMPORT_BY_NAME )RvaToVa( rvaName );

                            if ( pName )
                            {
                                pImportName->m_pszOrig
                                    = DuplicateString( ( PCHAR )pName->Name );

                                if ( pImportName->m_pszOrig == NULL )
                                {
                                    goto fail;
                                }
                            }
                        }
                    }
                }
            }

            oidp++;
        }

        ////////////////////////////////////////////////////////// Parse Sections.
        //
        m_nExtraOffset = 0;

        for ( n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++ )
        {
            m_nExtraOffset = Max( m_SectionHeaders[n].PointerToRawData +
                                  m_SectionHeaders[n].SizeOfRawData,
                                  m_nExtraOffset );

            if ( strcmp( ( PCHAR )m_SectionHeaders[n].Name, ".detour" ) == 0 )
            {
                DETOUR_SECTION_HEADER dh;
                CopyMemory( &dh,
                            m_pMap + m_SectionHeaders[n].PointerToRawData,
                            sizeof( dh ) );

                if ( dh.nDataOffset == 0 )
                {
                    dh.nDataOffset = dh.cbHeaderSize;
                }

                cbData = dh.cbDataSize - dh.nDataOffset;
                pbData = ( m_pMap +
                           m_SectionHeaders[n].PointerToRawData +
                           dh.nDataOffset );

                m_nExtraOffset = Max( m_SectionHeaders[n].PointerToRawData +
                                      m_SectionHeaders[n].SizeOfRawData,
                                      m_nExtraOffset );

                m_NtHeader.FileHeader.NumberOfSections--;

                m_NtHeader.OptionalHeader
                .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
                    = dh.nOriginalImportVirtualAddress;
                m_NtHeader.OptionalHeader
                .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size
                    = dh.nOriginalImportSize;

                m_NtHeader.OptionalHeader
                .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress
                    = dh.nOriginalBoundImportVirtualAddress;
                m_NtHeader.OptionalHeader
                .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size
                    = dh.nOriginalBoundImportSize;

                m_NtHeader.OptionalHeader
                .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress
                    = dh.nOriginalIatVirtualAddress;
                m_NtHeader.OptionalHeader
                .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size
                    = dh.nOriginalIatSize;

                m_NtHeader.OptionalHeader.CheckSum = 0;
                m_NtHeader.OptionalHeader.SizeOfImage
                    = dh.nOriginalSizeOfImage;

                m_fHadDetourSection = TRUE;
            }
        }

        m_pImageData = new CImageData( pbData, cbData );

        if ( m_pImageData == NULL )
        {
            SetLastError( ERROR_OUTOFMEMORY );
        }

        return TRUE;

fail:
        return FALSE;
    }

    static inline BOOL strneq( __in_z PCHAR pszOne, __in_z PCHAR pszTwo )
    {
        if ( pszOne == pszTwo )
        {
            return FALSE;
        }

        if ( !pszOne || !pszTwo )
        {
            return TRUE;
        }

        return ( strcmp( pszOne, pszTwo ) != 0 );
    }

    BOOL CImage::CheckImportsNeeded( DWORD *pnTables, DWORD *pnThunks, DWORD *pnChars )
    {
        DWORD nTables = 0;
        DWORD nThunks = 0;
        DWORD nChars = 0;
        BOOL fNeedDetourSection = FALSE;

        for ( CImageImportFile *pImportFile = m_pImportFiles;
                pImportFile != NULL; pImportFile = pImportFile->m_pNextFile )
        {

            nChars += ( int )strlen( pImportFile->m_pszName ) + 1;
            nChars += nChars & 1;

            if ( pImportFile->m_fByway )
            {
                fNeedDetourSection = TRUE;
                nThunks++;
            }
            else
            {
                if ( !fNeedDetourSection &&
                        strneq( pImportFile->m_pszName, pImportFile->m_pszOrig ) )
                {

                    fNeedDetourSection = TRUE;
                }

                for ( DWORD n = 0; n < pImportFile->m_nImportNames; n++ )
                {
                    CImageImportName *pImportName = &pImportFile->m_pImportNames[n];

                    if ( !fNeedDetourSection &&
                            strneq( pImportName->m_pszName, pImportName->m_pszOrig ) )
                    {

                        fNeedDetourSection = TRUE;
                    }

                    if ( pImportName->m_pszName )
                    {
                        nChars += sizeof( WORD );           // Hint
                        nChars += ( int )strlen( pImportName->m_pszName ) + 1;
                        nChars += nChars & 1;
                    }

                    nThunks++;
                }
            }

            nThunks++;
            nTables++;
        }

        nTables++;

        *pnTables = nTables;
        *pnThunks = nThunks;
        *pnChars = nChars;

        return fNeedDetourSection;
    }

//////////////////////////////////////////////////////////////////////////////
//
    CImageImportFile * CImage::NewByway( __in_z PCHAR pszName )
    {
        CImageImportFile *pImportFile = new CImageImportFile;

        if ( pImportFile == NULL )
        {
            SetLastError( ERROR_OUTOFMEMORY );
            goto fail;
        }

        pImportFile->m_pNextFile = NULL;
        pImportFile->m_fByway = TRUE;

        pImportFile->m_pszName = DuplicateString( pszName );

        if ( pImportFile->m_pszName == NULL )
        {
            goto fail;
        }

        pImportFile->m_rvaOriginalFirstThunk = 0;
        pImportFile->m_rvaFirstThunk = 0;
        pImportFile->m_nForwarderChain = ( UINT )0;
        pImportFile->m_pImportNames = NULL;
        pImportFile->m_nImportNames = 0;

        m_nImportFiles++;
        return pImportFile;

fail:

        if ( pImportFile )
        {
            delete pImportFile;
            pImportFile = NULL;
        }

        return NULL;
    }

    BOOL CImage::EditImports( PVOID pContext,
                              PF_DETOUR_BINARY_BYWAY_CALLBACK pfBywayCallback,
                              PF_DETOUR_BINARY_FILE_CALLBACK pfFileCallback,
                              PF_DETOUR_BINARY_SYMBOL_CALLBACK pfSymbolCallback,
                              PF_DETOUR_BINARY_COMMIT_CALLBACK pfCommitCallback )
    {
        CImageImportFile *pImportFile = NULL;
        CImageImportFile **ppLastFile = &m_pImportFiles;

        SetLastError( ERROR_CALL_NOT_IMPLEMENTED );

        while ( ( pImportFile = *ppLastFile ) != NULL )
        {

            if ( pfBywayCallback )
            {
                PCHAR pszFile = NULL;

                if ( !( *pfBywayCallback )( pContext, pszFile, &pszFile ) )
                {
                    goto fail;
                }

                if ( pszFile )
                {
                    // Insert a new Byway.
                    CImageImportFile *pByway = NewByway( pszFile );

                    if ( pByway == NULL )
                    {
                        return FALSE;
                    }

                    pByway->m_pNextFile = pImportFile;
                    *ppLastFile = pByway;
                    ppLastFile = &pByway->m_pNextFile;
                    continue;                               // Retry after Byway.
                }
            }

            if ( pImportFile->m_fByway )
            {
                if ( pfBywayCallback )
                {
                    PCHAR pszFile = pImportFile->m_pszName;

                    if ( !( *pfBywayCallback )( pContext, pszFile, &pszFile ) )
                    {
                        goto fail;
                    }

                    if ( pszFile )                          // Replace? Byway
                    {
                        if ( ReplaceString( &pImportFile->m_pszName, pszFile ) == NULL )
                        {
                            goto fail;
                        }
                    }
                    else                                    // Delete Byway
                    {
                        *ppLastFile = pImportFile->m_pNextFile;
                        pImportFile->m_pNextFile = NULL;
                        delete pImportFile;
                        pImportFile = *ppLastFile;
                        m_nImportFiles--;
                        continue;                           // Retry after delete.
                    }
                }
            }
            else
            {
                if ( pfFileCallback )
                {
                    PCHAR pszFile = pImportFile->m_pszName;

                    if ( !( *pfFileCallback )( pContext, pImportFile->m_pszOrig,
                                               pszFile, &pszFile ) )
                    {
                        goto fail;
                    }

                    if ( pszFile != NULL )
                    {
                        if ( ReplaceString( &pImportFile->m_pszName, pszFile ) == NULL )
                        {
                            goto fail;
                        }
                    }
                }

                if ( pfSymbolCallback )
                {
                    for ( DWORD n = 0; n < pImportFile->m_nImportNames; n++ )
                    {
                        CImageImportName *pImportName = &pImportFile->m_pImportNames[n];

                        PCHAR pszName = pImportName->m_pszName;
                        ULONG nOrdinal = pImportName->m_nOrdinal;

                        if ( !( *pfSymbolCallback )( pContext,
                                                     pImportName->m_nOrig,
                                                     nOrdinal,
                                                     &nOrdinal,
                                                     pImportName->m_pszOrig,
                                                     pszName,
                                                     &pszName ) )
                        {
                            goto fail;
                        }

                        if ( pszName != NULL )
                        {
                            pImportName->m_nOrdinal = 0;

                            if ( ReplaceString( &pImportName->m_pszName, pszName ) == NULL )
                            {
                                goto fail;
                            }
                        }
                        else if ( nOrdinal != 0 )
                        {
                            pImportName->m_nOrdinal = nOrdinal;

                            if ( pImportName->m_pszName != NULL )
                            {
                                delete[] pImportName->m_pszName;
                                pImportName->m_pszName = NULL;
                            }
                        }
                    }
                }
            }

            ppLastFile = &pImportFile->m_pNextFile;
            pImportFile = pImportFile->m_pNextFile;
        }

        for ( ;; )
        {
            if ( pfBywayCallback )
            {
                PCHAR pszFile = NULL;

                if ( !( *pfBywayCallback )( pContext, NULL, &pszFile ) )
                {
                    goto fail;
                }

                if ( pszFile )
                {
                    // Insert a new Byway.
                    CImageImportFile *pByway = NewByway( pszFile );

                    if ( pByway == NULL )
                    {
                        return FALSE;
                    }

                    pByway->m_pNextFile = pImportFile;
                    *ppLastFile = pByway;
                    ppLastFile = &pByway->m_pNextFile;
                    continue;                               // Retry after Byway.
                }
            }

            break;
        }

        if ( pfCommitCallback )
        {
            if ( !( *pfCommitCallback )( pContext ) )
            {
                goto fail;
            }
        }

        SetLastError( NO_ERROR );
        return TRUE;

fail:
        return FALSE;
    }

    BOOL CImage::Write( HANDLE hFile )
    {
        DWORD cbDone;

        if ( hFile == INVALID_HANDLE_VALUE )
        {
            SetLastError( ERROR_INVALID_HANDLE );
            return FALSE;
        }

        m_nNextFileAddr = 0;
        m_nNextVirtAddr = 0;

        DWORD nTables = 0;
        DWORD nThunks = 0;
        DWORD nChars = 0;
        BOOL fNeedDetourSection = CheckImportsNeeded( &nTables, &nThunks, &nChars );

        //////////////////////////////////////////////////////////// Copy Headers.
        //
        if ( SetFilePointer( hFile, 0, NULL, FILE_BEGIN ) == ~0u )
        {
            return FALSE;
        }

        if ( !CopyFileData( hFile, 0, m_NtHeader.OptionalHeader.SizeOfHeaders ) )
        {
            return FALSE;
        }

        if ( fNeedDetourSection || !m_pImageData->IsEmpty() )
        {
            // Replace the file's DOS header with our own.
            m_nPeOffset = sizeof( m_DosHeader ) + sizeof( s_rbDosCode );
            m_nSectionsOffset = m_nPeOffset
                                + sizeof( m_NtHeader.Signature )
                                + sizeof( m_NtHeader.FileHeader )
                                + m_NtHeader.FileHeader.SizeOfOptionalHeader;
            m_DosHeader.e_lfanew = m_nPeOffset;

            if ( SetFilePointer( hFile, 0, NULL, FILE_BEGIN ) == ~0u )
            {
                return FALSE;
            }

            if ( !WriteFile( hFile, &m_DosHeader, sizeof( m_DosHeader ), &cbDone ) )
            {
                return FALSE;
            }

            if ( !WriteFile( hFile, &s_rbDosCode, sizeof( s_rbDosCode ), &cbDone ) )
            {
                return FALSE;
            }
        }
        else
        {
            // Restore the file's original DOS header.
            if ( m_nPrePE != 0 )
            {
                m_nPeOffset = m_cbPrePE;
                m_nSectionsOffset = m_nPeOffset
                                    + sizeof( m_NtHeader.Signature )
                                    + sizeof( m_NtHeader.FileHeader )
                                    + m_NtHeader.FileHeader.SizeOfOptionalHeader;
                m_DosHeader.e_lfanew = m_nPeOffset;


                if ( SetFilePointer( hFile, 0, NULL, FILE_BEGIN ) == ~0u )
                {
                    return FALSE;
                }

                if ( !CopyFileData( hFile, m_nPrePE, m_cbPrePE ) )
                {
                    return FALSE;
                }
            }
        }

        m_nNextFileAddr = m_NtHeader.OptionalHeader.SizeOfHeaders;
        m_nNextVirtAddr = 0;

        if ( !AlignFileData( hFile ) )
        {
            return FALSE;
        }

        /////////////////////////////////////////////////////////// Copy Sections.
        //
        DWORD n = 0;

        for ( ; n < m_NtHeader.FileHeader.NumberOfSections; n++ )
        {
            if ( m_SectionHeaders[n].SizeOfRawData )
            {
                if ( SetFilePointer( hFile,
                                     m_SectionHeaders[n].PointerToRawData,
                                     NULL, FILE_BEGIN ) == ~0u )
                {
                    return FALSE;
                }

                if ( !CopyFileData( hFile,
                                    m_SectionHeaders[n].PointerToRawData,
                                    m_SectionHeaders[n].SizeOfRawData ) )
                {
                    return FALSE;
                }
            }

            m_nNextFileAddr = Max( m_SectionHeaders[n].PointerToRawData +
                                   m_SectionHeaders[n].SizeOfRawData,
                                   m_nNextFileAddr );
            m_nNextVirtAddr = Max( m_SectionHeaders[n].VirtualAddress +
                                   m_SectionHeaders[n].Misc.VirtualSize,
                                   m_nNextVirtAddr );
            m_nExtraOffset = Max( m_nNextFileAddr, m_nExtraOffset );

            if ( !AlignFileData( hFile ) )
            {
                return FALSE;
            }
        }

        if ( fNeedDetourSection || !m_pImageData->IsEmpty() )
        {
            ////////////////////////////////////////////// Insert .detour Section.
            //
            DWORD nSection = m_NtHeader.FileHeader.NumberOfSections++;
            DETOUR_SECTION_HEADER dh;

            ZeroMemory( &dh, sizeof( dh ) );
            ZeroMemory( &m_SectionHeaders[nSection], sizeof( m_SectionHeaders[nSection] ) );

            dh.cbHeaderSize = sizeof( DETOUR_SECTION_HEADER );
            dh.nSignature = DETOUR_SECTION_HEADER_SIGNATURE;

            dh.nOriginalImportVirtualAddress = m_NtHeader.OptionalHeader
                                               .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
            dh.nOriginalImportSize = m_NtHeader.OptionalHeader
                                     .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;

            dh.nOriginalBoundImportVirtualAddress
                = m_NtHeader.OptionalHeader
                  .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress;
            dh.nOriginalBoundImportSize = m_NtHeader.OptionalHeader
                                          .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size;

            dh.nOriginalIatVirtualAddress = m_NtHeader.OptionalHeader
                                            .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress;
            dh.nOriginalIatSize = m_NtHeader.OptionalHeader
                                  .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size;

            dh.nOriginalSizeOfImage = m_NtHeader.OptionalHeader.SizeOfImage;

            DWORD clrAddr = m_NtHeader.OptionalHeader
                            .DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress;
            DWORD clrSize = m_NtHeader.OptionalHeader
                            .DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size;

            if ( clrAddr && clrSize )
            {
                PDETOUR_CLR_HEADER pHdr = ( PDETOUR_CLR_HEADER )RvaToVa( clrAddr );

                if ( pHdr != NULL )
                {
                    DETOUR_CLR_HEADER hdr;
                    hdr = *pHdr;

                    dh.nOriginalClrFlags = hdr.Flags;
                }
            }

            HRESULT hrRet = StringCchCopyA( ( PCHAR )m_SectionHeaders[nSection].Name, IMAGE_SIZEOF_SHORT_NAME , ".detour" );

            if ( FAILED( hrRet ) )
                return FALSE;

            m_SectionHeaders[nSection].Characteristics
                = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;

            m_nOutputVirtAddr = m_nNextVirtAddr;
            m_nOutputVirtSize = 0;
            m_nOutputFileAddr = m_nNextFileAddr;

            dh.nDataOffset = 0;                     // pbData
            dh.cbDataSize = m_pImageData->m_cbData;
            dh.cbPrePE = m_cbPrePE;

            //////////////////////////////////////////////////////////////////////////
            //

            DWORD rvaImportTable = 0;
            DWORD rvaLookupTable = 0;
            DWORD rvaBoundTable = 0;
            DWORD rvaNameTable = 0;
            DWORD nImportTableSize = nTables * sizeof( IMAGE_IMPORT_DESCRIPTOR );

            if ( !SizeOutputBuffer( QuadAlign( sizeof( dh ) )
                                    + m_cbPrePE
                                    + QuadAlign( m_pImageData->m_cbData )
                                    + QuadAlign( sizeof( IMAGE_THUNK_DATA ) * nThunks )
                                    + QuadAlign( sizeof( IMAGE_THUNK_DATA ) * nThunks )
                                    + QuadAlign( nChars )
                                    + QuadAlign( nImportTableSize ) ) )
            {
                return FALSE;
            }

            DWORD vaHead = 0;
            PBYTE pbHead = NULL;
            DWORD vaPrePE = 0;
            PBYTE pbPrePE = NULL;
            DWORD vaData = 0;
            PBYTE pbData = NULL;

            if ( ( pbHead = AllocateOutput( sizeof( dh ), &vaHead ) ) == NULL )
            {
                return FALSE;
            }

            if ( ( pbPrePE = AllocateOutput( m_cbPrePE, &vaPrePE ) ) == NULL )
            {
                return FALSE;
            }

            CImageThunks lookupTable( this, nThunks, &rvaLookupTable );
            CImageThunks boundTable( this, nThunks, &rvaBoundTable );
            CImageChars nameTable( this, nChars, &rvaNameTable );

            if ( ( pbData = AllocateOutput( m_pImageData->m_cbData, &vaData ) ) == NULL )
            {
                return FALSE;
            }

            dh.nDataOffset = vaData - vaHead;
            dh.cbDataSize = dh.nDataOffset + m_pImageData->m_cbData;
            CopyMemory( pbHead, &dh, sizeof( dh ) );
            CopyMemory( pbPrePE, m_pMap + m_nPrePE, m_cbPrePE );
            CopyMemory( pbData, m_pImageData->m_pbData, m_pImageData->m_cbData );

            PIMAGE_IMPORT_DESCRIPTOR piidDst = ( PIMAGE_IMPORT_DESCRIPTOR )
                                               AllocateOutput( nImportTableSize, &rvaImportTable );

            if ( piidDst == NULL )
            {
                return FALSE;
            }

            //////////////////////////////////////////////// Step Through Imports.
            //
            for ( CImageImportFile *pImportFile = m_pImportFiles;
                    pImportFile != NULL; pImportFile = pImportFile->m_pNextFile )
            {

                ZeroMemory( piidDst, sizeof( piidDst ) );
                nameTable.Allocate( pImportFile->m_pszName, ( DWORD * )&piidDst->Name );
                piidDst->TimeDateStamp = 0;
                piidDst->ForwarderChain = pImportFile->m_nForwarderChain;

                if ( pImportFile->m_fByway )
                {
                    ULONG rvaIgnored;

                    lookupTable.Allocate( IMAGE_ORDINAL_FLAG + 1,
                                          ( DWORD * )&piidDst->OriginalFirstThunk );
                    boundTable.Allocate( IMAGE_ORDINAL_FLAG + 1,
                                         ( DWORD * )&piidDst->FirstThunk );

                    lookupTable.Allocate( 0, &rvaIgnored );
                    boundTable.Allocate( 0, &rvaIgnored );
                }
                else
                {
                    ULONG rvaIgnored;

                    piidDst->FirstThunk = ( ULONG )pImportFile->m_rvaFirstThunk;
                    lookupTable.Current( ( DWORD * )&piidDst->OriginalFirstThunk );

                    for ( n = 0; n < pImportFile->m_nImportNames; n++ )
                    {
                        CImageImportName *pImportName = &pImportFile->m_pImportNames[n];

                        if ( pImportName->m_pszName )
                        {
                            ULONG nDstName = 0;

                            nameTable.Allocate( pImportName->m_pszName,
                                                pImportName->m_nHint,
                                                &nDstName );
                            lookupTable.Allocate( nDstName, &rvaIgnored );
                        }
                        else
                        {
                            lookupTable.Allocate( IMAGE_ORDINAL_FLAG + pImportName->m_nOrdinal,
                                                  &rvaIgnored );
                        }
                    }

                    lookupTable.Allocate( 0, &rvaIgnored );
                }

                piidDst++;
            }

            ZeroMemory( piidDst, sizeof( piidDst ) );

            //////////////////////////////////////////////////////////////////////////
            //
            m_nNextVirtAddr += m_nOutputVirtSize;
            m_nNextFileAddr += FileAlign( m_nOutputVirtSize );

            if ( !AlignFileData( hFile ) )
            {
                return FALSE;
            }

            //////////////////////////////////////////////////////////////////////////
            //
            m_SectionHeaders[nSection].VirtualAddress = m_nOutputVirtAddr;
            m_SectionHeaders[nSection].Misc.VirtualSize = m_nOutputVirtSize;
            m_SectionHeaders[nSection].PointerToRawData = m_nOutputFileAddr;
            m_SectionHeaders[nSection].SizeOfRawData = FileAlign( m_nOutputVirtSize );

            m_NtHeader.OptionalHeader
            .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
                = rvaImportTable;
            m_NtHeader.OptionalHeader
            .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size
                = nImportTableSize;

            m_NtHeader.OptionalHeader
            .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
            m_NtHeader.OptionalHeader
            .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;

            //////////////////////////////////////////////////////////////////////////
            //
            if ( SetFilePointer( hFile, m_SectionHeaders[nSection].PointerToRawData,
                                 NULL, FILE_BEGIN ) == ~0u )
            {
                return FALSE;
            }

            if ( !WriteFile( hFile, m_pbOutputBuffer, m_SectionHeaders[nSection].SizeOfRawData,
                             &cbDone ) )
            {
                return FALSE;
            }
        }

        ///////////////////////////////////////////////////// Adjust Extra Data.
        //
        LONG nExtraAdjust = m_nNextFileAddr - m_nExtraOffset;

        for ( n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++ )
        {
            if ( m_SectionHeaders[n].PointerToRawData > m_nExtraOffset )
            {
                m_SectionHeaders[n].PointerToRawData += nExtraAdjust;
            }

            if ( m_SectionHeaders[n].PointerToRelocations > m_nExtraOffset )
            {
                m_SectionHeaders[n].PointerToRelocations += nExtraAdjust;
            }

            if ( m_SectionHeaders[n].PointerToLinenumbers > m_nExtraOffset )
            {
                m_SectionHeaders[n].PointerToLinenumbers += nExtraAdjust;
            }
        }

        if ( m_NtHeader.FileHeader.PointerToSymbolTable > m_nExtraOffset )
        {
            m_NtHeader.FileHeader.PointerToSymbolTable += nExtraAdjust;
        }

        m_NtHeader.OptionalHeader.CheckSum = 0;
        m_NtHeader.OptionalHeader.SizeOfImage = m_nNextVirtAddr;

        ////////////////////////////////////////////////// Adjust Debug Directory.
        //
        DWORD debugAddr = m_NtHeader.OptionalHeader
                          .DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
        DWORD debugSize = m_NtHeader.OptionalHeader
                          .DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;

        if ( debugAddr && debugSize )
        {
            DWORD nFileOffset = RvaToFileOffset( debugAddr );

            if ( SetFilePointer( hFile, nFileOffset, NULL, FILE_BEGIN ) == ~0u )
            {
                return FALSE;
            }

            PIMAGE_DEBUG_DIRECTORY pDir = ( PIMAGE_DEBUG_DIRECTORY )RvaToVa( debugAddr );

            if ( pDir == NULL )
            {
                return FALSE;
            }

            DWORD nEntries = debugSize / sizeof( *pDir );

            for ( n = 0; n < nEntries; n++ )
            {
                IMAGE_DEBUG_DIRECTORY dir = pDir[n];

                if ( dir.PointerToRawData > m_nExtraOffset )
                {
                    dir.PointerToRawData += nExtraAdjust;
                }

                if ( !WriteFile( hFile, &dir, sizeof( dir ), &cbDone ) )
                {
                    return FALSE;
                }
            }
        }

        /////////////////////////////////////////////////////// Adjust CLR Header.
        //
        DWORD clrAddr = m_NtHeader.OptionalHeader
                        .DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress;
        DWORD clrSize = m_NtHeader.OptionalHeader
                        .DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size;

        if ( clrAddr && clrSize && fNeedDetourSection )
        {
            DWORD nFileOffset = RvaToFileOffset( clrAddr );

            if ( SetFilePointer( hFile, nFileOffset, NULL, FILE_BEGIN ) == ~0u )
            {
                return FALSE;
            }

            PDETOUR_CLR_HEADER pHdr = ( PDETOUR_CLR_HEADER )RvaToVa( clrAddr );

            if ( pHdr == NULL )
            {
                return FALSE;
            }

            DETOUR_CLR_HEADER hdr;
            hdr = *pHdr;
            hdr.Flags &= 0xfffffffe;    // Clear the IL_ONLY flag.

            if ( !WriteFile( hFile, &hdr, sizeof( hdr ), &cbDone ) )
            {
                return FALSE;
            }
        }

        ///////////////////////////////////////////////// Copy Left-over Data.
        //
        if ( m_nFileSize > m_nExtraOffset )
        {
            if ( SetFilePointer( hFile, m_nNextFileAddr, NULL, FILE_BEGIN ) == ~0u )
            {
                return FALSE;
            }

            if ( !CopyFileData( hFile, m_nExtraOffset, m_nFileSize - m_nExtraOffset ) )
            {
                return FALSE;
            }
        }


        //////////////////////////////////////////////////// Finalize Headers.
        //

        if ( SetFilePointer( hFile, m_nPeOffset, NULL, FILE_BEGIN ) == ~0u )
        {
            return FALSE;
        }

        if ( !WriteFile( hFile, &m_NtHeader, sizeof( m_NtHeader ), &cbDone ) )
        {
            return FALSE;
        }

        if ( SetFilePointer( hFile, m_nSectionsOffset, NULL, FILE_BEGIN ) == ~0u )
        {
            return FALSE;
        }

        if ( !WriteFile( hFile, &m_SectionHeaders,
                         sizeof( m_SectionHeaders[0] )
                         * m_NtHeader.FileHeader.NumberOfSections,
                         &cbDone ) )
        {
            return FALSE;
        }

        m_cbPostPE = SetFilePointer( hFile, 0, NULL, FILE_CURRENT );

        if ( m_cbPostPE == ~0u )
        {
            return FALSE;
        }

        m_cbPostPE = m_NtHeader.OptionalHeader.SizeOfHeaders - m_cbPostPE;

        return TRUE;
    }

};                                                      // namespace Detour


//////////////////////////////////////////////////////////////////////////////
//
PDETOUR_BINARY WINAPI DetourBinaryOpen( HANDLE hFile )
{
    Detour::CImage *pImage = new Detour::CImage;

    if ( pImage == NULL )
    {
        SetLastError( ERROR_OUTOFMEMORY );
        return FALSE;
    }

    if ( !pImage->Read( hFile ) )
    {
        delete pImage;
        return FALSE;
    }

    return ( PDETOUR_BINARY )pImage;
}

BOOL WINAPI DetourBinaryWrite( PDETOUR_BINARY pdi, HANDLE hFile )
{
    Detour::CImage *pImage = Detour::CImage::IsValid( pdi );

    if ( pImage == NULL )
    {
        return FALSE;
    }

    return pImage->Write( hFile );
}

PVOID WINAPI DetourBinaryEnumeratePayloads( PDETOUR_BINARY pdi,
        GUID *pGuid,
        DWORD *pcbData,
        DWORD *pnIterator )
{
    Detour::CImage *pImage = Detour::CImage::IsValid( pdi );

    if ( pImage == NULL )
    {
        return FALSE;
    }

    return pImage->DataEnum( pGuid, pcbData, pnIterator );
}

PVOID WINAPI DetourBinaryFindPayload( PDETOUR_BINARY pdi,
                                      REFGUID rguid,
                                      DWORD *pcbData )
{
    Detour::CImage *pImage = Detour::CImage::IsValid( pdi );

    if ( pImage == NULL )
    {
        return FALSE;
    }

    return pImage->DataFind( rguid, pcbData );
}

PVOID WINAPI DetourBinarySetPayload( PDETOUR_BINARY pdi,
                                     REFGUID rguid,
                                     PVOID pvData,
                                     DWORD cbData )
{
    Detour::CImage *pImage = Detour::CImage::IsValid( pdi );

    if ( pImage == NULL )
    {
        return FALSE;
    }

    return pImage->DataSet( rguid, ( PBYTE )pvData, cbData );
}

BOOL WINAPI DetourBinaryDeletePayload( PDETOUR_BINARY pdi,
                                       REFGUID rguid )
{
    Detour::CImage *pImage = Detour::CImage::IsValid( pdi );

    if ( pImage == NULL )
    {
        return FALSE;
    }

    return pImage->DataDelete( rguid );
}

BOOL WINAPI DetourBinaryPurgePayloads( PDETOUR_BINARY pdi )
{
    Detour::CImage *pImage = Detour::CImage::IsValid( pdi );

    if ( pImage == NULL )
    {
        return FALSE;
    }

    return pImage->DataPurge();
}

//////////////////////////////////////////////////////////////////////////////
//
static BOOL CALLBACK ResetBywayCallback( PVOID pContext,
        __in_z PCHAR pszFile,
        __deref PCHAR *ppszOutFile )
{
    ( void )pContext;
    ( void )pszFile;

    *ppszOutFile = NULL;
    return TRUE;
}

static BOOL CALLBACK ResetFileCallback( PVOID pContext,
                                        __in_z PCHAR pszOrigFile,
                                        __in_z PCHAR pszFile,
                                        __deref PCHAR *ppszOutFile )
{
    ( void )pContext;
    ( void )pszFile;

    *ppszOutFile = pszOrigFile;
    return TRUE;
}

static BOOL CALLBACK ResetSymbolCallback( PVOID pContext,
        ULONG nOrigOrdinal,
        ULONG nOrdinal,
        ULONG *pnOutOrdinal,
        __in_z PCHAR pszOrigSymbol,
        __in_z PCHAR pszSymbol,
        __deref PCHAR *ppszOutSymbol )
{
    ( void )pContext;
    ( void )nOrdinal;
    ( void )pszSymbol;

    *pnOutOrdinal = nOrigOrdinal;
    *ppszOutSymbol = pszOrigSymbol;
    return TRUE;
}

BOOL WINAPI DetourBinaryResetImports( PDETOUR_BINARY pdi )
{
    Detour::CImage *pImage = Detour::CImage::IsValid( pdi );

    if ( pImage == NULL )
    {
        return FALSE;
    }

    return pImage->EditImports( NULL,
                                ResetBywayCallback,
                                ResetFileCallback,
                                ResetSymbolCallback,
                                NULL );
}

//////////////////////////////////////////////////////////////////////////////
//
BOOL WINAPI DetourBinaryEditImports( PDETOUR_BINARY pdi,
                                     PVOID pContext,
                                     PF_DETOUR_BINARY_BYWAY_CALLBACK pfBywayCallback,
                                     PF_DETOUR_BINARY_FILE_CALLBACK pfFileCallback,
                                     PF_DETOUR_BINARY_SYMBOL_CALLBACK pfSymbolCallback,
                                     PF_DETOUR_BINARY_COMMIT_CALLBACK pfCommitCallback )
{
    Detour::CImage *pImage = Detour::CImage::IsValid( pdi );

    if ( pImage == NULL )
    {
        return FALSE;
    }

    return pImage->EditImports( pContext,
                                pfBywayCallback,
                                pfFileCallback,
                                pfSymbolCallback,
                                pfCommitCallback );
}

BOOL WINAPI DetourBinaryClose( PDETOUR_BINARY pdi )
{
    Detour::CImage *pImage = Detour::CImage::IsValid( pdi );

    if ( pImage == NULL )
    {
        return FALSE;
    }

    BOOL bSuccess = pImage->Close();
    delete pImage;
    pImage = NULL;

    return bSuccess;
}

//
///////////////////////////////////////////////////////////////// End of File.
